
#include "subject.hpp"

Subject::Subject() {}

Subject::~Subject() {
    for ( auto listener : __obs ) {
        listener->__state = 0;
    }
    for( auto listener : __subs ) {
        listener->__state = 0;
    }
}

void Subject::off( Subject * ober, std::string const & eventName, SubjectCallback cb )
{
    std::vector<Listener*> &ls = __obs;
    for ( auto iter = ls.begin(); iter != ls.end(); iter++ )
    {
        Listener& listener = *( *iter );
        if ( !listener.__state ) {
            __unuseCount++;
            continue;
        }
        if ( listener.ober != ober ) continue;
        if ( cb && listener.cb != cb ) continue;
        if ( !eventName.empty() && listener.eventName != eventName ) continue;

        listener.__state = 0;
        __unuseCount++;
        break;
    }

    if ( __unuseCount > 0 ) {
        __listenerGc( );
    }
}

void Subject::emit( std::string const & eventName, Message * msg )
{
    auto iter = __obs.begin();
    auto end = __obs.end();

    while ( iter != end ) {
        Listener& listener = *( *iter );
        if ( !listener.__state ) {
            __unuseCount++;
            iter++;
            continue;
        }
        if ( listener.eventName != eventName ) {
            iter++;
            continue;
        }

        ( listener.ober->*listener.cb )( msg, eventName );
        iter++;
    }
    if (__unuseCount > 5 ) {
        __listenerGc( );
    }
}

void Subject::__listenerGc( )
{
    auto iter = __obs.begin();
    while ( iter != __obs.end() ) {
        if ( !(*iter)->__state ) {
            iter = __obs.erase( iter );
        }
        else {
            iter++;
        }
    }
    __unuseCount = 0;
}

void Subject::remit( Message * msg, std::string const & eventName ) {
    this->emit( eventName, msg );
}

std::vector< std::unique_ptr<Listener> > Listener::__pool;

Listener::Listener( std::string const & eventName, SubjectCallback cb, Subject * ober ) {
    this->eventName = eventName;
    this->cb = cb;
    this->ober = ober;

    Listener::__pool.push_back( std::unique_ptr<Listener>( this ) );
}
